package task

import (
	"errors"
	"gitee.com/saxon134/sysmain/db/models"
	"gitee.com/saxon134/sysmain/errs"
	"github.com/saxon134/go-utils/saData"
	"github.com/saxon134/go-utils/saData/saTime"
	"github.com/saxon134/go-utils/saData/saUrl"
	"github.com/saxon134/go-utils/saHttp"
	"github.com/saxon134/go-utils/saLog"
	"time"
)

// ///////////////////////////////////////////////////////
//
//	0/30 * * * * *                        every 30s
//	0 43 21 * * *                         21:43
//	0 15 05 * * * 　　                    05:15
//	0 0 17 * * *                          17:00
//	0 0 17 * * 1                          17:00 in every Monday
//	0 0,10 17 * * 0,2,3                   17:00 and 17:10 in every Sunday, Tuesday and Wednesday
//	0 0-10 17 1 * *                       17:00 to 17:10 in 1 min duration each time on the first day of month
//	0 0 0 1,15 * 1                        0:00 on the 1st day and 15th day of month
//	0 42 4 1 * * 　 　                    4:42 on the 1st day of month
//	0 0 21 * * 1-6　　                    21:00 from Monday to Saturday
//	0 0,10,20,30,40,50 * * * *　          every 10 min duration
//	0 */10 * * * * 　　　　　　           every 10 min duration
//	0 * 1 * * *　　　　　　　　           1:00 to 1:59 in 1 min duration each time
//	0 0 1 * * *　　　　　　　　           1:00
//	0 0 */1 * * *　　　　　　　           0 min of hour in 1 hour duration
//	0 0 * * * *　　　　　　　　           0 min of hour in 1 hour duration
//	0 2 8-20/3 * * *　　　　　　          8:02, 11:02, 14:02, 17:02, 20:02
//	0 30 5 1,15 * *　　　　　　           5:30 on the 1st day and 15th day of month
//
// ///////////////////////////////////////////////////////

type Client struct {
	app        string
	host       string
	port       int
	sysmainUrl string
	secret     string
}

type Case struct {
	Key     string //任务唯一标识
	Spec    string
	Local   bool //local为true，代表为本地任务，不注册到sysmain，此时Spec非空，注册后会立即启动
	Handler Handler
	Params  string
}

type Handler func(params string) error

var _client *Client
var _cases []Case

// Init
// remoteUrl sysmain http接口地址
// secret sysmain http接口秘钥
// host 本机地址
// port 本机服务端口
func Init(app string, sysmainUrl string, secret string, host string, port int) *Client {
	_client = &Client{
		app:        app,
		host:       host,
		port:       port,
		sysmainUrl: sysmainUrl,
		secret:     secret,
	}
	return _client
}

// Register 注册任务
func (m *Client) Register(cases ...Case) {
	if _client == nil {
		panic("请先调用Init接口初始化")
	}

	_cases = cases
	if len(cases) == 0 {
		return
	}

	var taskKeys = ""
	for _, c := range cases {
		if c.Local == true {
			if c.Spec == "" {
				panic("spec can not be empty for local task")
			}
			AddTask(c.Key, NewTask(c.Key, c.Spec, c.Handler))
		} else {
			taskKeys += c.Key + ","
		}
	}

	//开启任务
	StartTask()

	go func() {
		//注册任务
		if taskKeys != "" {
			var headers = map[string]string{}
			if m.secret != "" {
				var timestamp = saData.I64tos(time.Now().Unix())
				headers["timestamp"] = timestamp
				headers["sign"] = saData.Md5(m.secret+timestamp, true)
			}
			_, err := saHttp.PostRequest(
				saUrl.ConnectUri(_client.sysmainUrl, "task/register"),
				map[string]string{
					"app": _client.app, "host": saUrl.QueryEncode(_client.host), "port": saData.String(_client.port),
					"keys": taskKeys,
				},
				headers,
			)
			if err != nil {
				saLog.Err("注册任务失败：" + err.Error())
			} else {
				saLog.Log("Task注册成功.")
			}
		}

		//等待3秒之后获取可执行任务
		time.Sleep(time.Second * 3)

		res, err := saHttp.Get(saUrl.ConnectUri(_client.sysmainUrl, "task/list"), map[string]string{
			"host": _client.host, "post": saData.String(_client.port),
		})
		if err != nil {
			saLog.Err("获取任务列表失败：", err.Error())
		}

		var result struct{ Result []*models.TblTask }
		_ = saData.StrToModel(res, &result)
		for _, task := range result.Result {
			if task.Status == 2 && task.Spec != "" && task.Key != "" {
				for _, h := range cases {
					if h.Key == task.Key {
						h.Params = task.Params
						AddTask(h.Key, NewTask(h.Key, task.Spec, h.Run))
						break
					}
				}
			}
		}
	}()
}

// Event
// Params:
//
//	Sign      string
//	Timestamp string
//	Key   string
//	Event string //start、stop、once
//	Spec  string
func (m *Client) Event(in map[string]string) (err error) {
	if in == nil || in["key"] == "" || in["event"] == "" {
		return errs.ErrorParams
	}

	if _client.secret != "" {
		if in["sign"] == "" || in["timestamp"] == "" {
			return errs.ErrorUnauthorized
		}

		var sign2 = saData.Md5(_client.secret+in["timestamp"], true)
		if sign2 != in["sign"] {
			return errs.ErrorUnauthorized
		}
	}

	if in["event"] == "start" {
		if AdminTaskList[in["key"]] == nil {
			if in["spec"] == "" {
				return errs.New("")
			}

			var handler *Case
			for _, h := range _cases {
				if h.Key == in["key"] {
					handler = &h
					break
				}
			}
			if handler == nil {
				return errors.New("该任务无对应处理接口")
			}
			AddTask(in["key"], NewTask(in["key"], in["spec"], handler.Run))
		}
	} else if in["event"] == "stop" {
		if AdminTaskList[in["key"]] != nil {
			DeleteTask(in["key"])
		}
	} else if in["event"] == "once" {
		var handle *Case
		for _, h := range _cases {
			if h.Key == in["key"] {
				handle = &h
				break
			}
		}
		if handle == nil {
			return errs.ErrorNotExisted
		}
		return once(handle, in["params"])
	}
	return nil
}

func (m *Client) Status(in map[string]string) (out map[string]string, err error) {
	if in == nil || in["key"] == "" {
		return nil, errs.ErrorParams
	}

	if _client.secret != "" {
		if in["sign"] == "" || in["timestamp"] == "" {
			return nil, errs.ErrorUnauthorized
		}

		var sign2 = saData.Md5(_client.secret+in["timestamp"], true)
		if sign2 != in["sign"] {
			return nil, errs.ErrorUnauthorized
		}
	}

	var t = AdminTaskList[in["key"]]
	if t == nil {
		return nil, errs.ErrorNotExisted
	}

	return map[string]string{
		"nextTime": saTime.TimeToStr(t.GetNext(), saTime.FormatDefault),
		"preTime":  saTime.TimeToStr(t.GetPrev(), saTime.FormatDefault),
		"errMsg":   t.GetStatus(),
	}, nil
}

func (m *Case) Run(params string) error {
	var start = time.Now()
	err := m.Handler(m.Params)
	var end = time.Now().UnixNano() / 1000

	//非本地任务，则向SDP发送执行结果
	if m.Local == false {
		var params = map[string]interface{}{
			"app":     _client.app,
			"key":     m.Key,
			"runner":  _client.host + ":" + saData.String(_client.port),
			"start":   start.Format(time.DateTime),
			"us":      end - start.UnixNano()/1000,
			"success": err == nil,
		}
		if err != nil {
			params["errMsg"] = err.Error()
		}
		_, _ = saHttp.PostJson(saUrl.ConnectUri(_client.sysmainUrl, "task/record/add"), params)
	}

	return err
}

func once(m *Case, params string) error {
	var start = time.Now()
	err := m.Handler(params)
	var end = time.Now().UnixNano() / 1000
	var result = map[string]interface{}{
		"app":    _client.app,
		"key":    m.Key,
		"runner": _client.host + ":" + saData.String(_client.port),
		"start":  start.Format(time.DateTime), "us": end - start.UnixNano()/1000,
		"success": err == nil,
	}
	if err != nil {
		result["errMsg"] = err.Error()
	}
	_, _ = saHttp.PostJson(saUrl.ConnectUri(_client.sysmainUrl, "task/record/add"), result)
	return err
}
